package org.springframework.validation.util;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.BeanWrapperImpl;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.HandlerMapping;

public class RequestUtils {
	
	public static final String PATH_VAR_PATTERN = "\\{[^\\}]+\\}";

	public static HttpServletRequest getRequest() {
		RequestAttributes attributes = RequestContextHolder.currentRequestAttributes();
		return ((ServletRequestAttributes) attributes).getRequest();
	}
	
	public static Map<String, Object> buildParameterMap(HttpServletRequest request) {
		Map<String, Object> parameterMap = new HashMap<String, Object>();
		Enumeration<String> parameterNames = request.getParameterNames();
		while (parameterNames.hasMoreElements()) {
			String parameterName = parameterNames.nextElement();
			parameterMap.put(parameterName, request.getParameter(parameterName));
		}
		return parameterMap;
	}
	
	public static Map<String, Object> buildAttributeMap(HttpServletRequest request) {
		Map<String, Object> attributeMap = new HashMap<String, Object>();
		Enumeration<String> attributeNames = request.getAttributeNames();
		while (attributeNames.hasMoreElements()) {
			String attributeName = attributeNames.nextElement();
			attributeMap.put(attributeName, request.getAttribute(attributeName));			
		}
		return attributeMap;
	}
	
	public static Map<String, Object> buildSessionMap(HttpServletRequest request) {
		Map<String, Object> sessionMap = new HashMap<String, Object>();
		Enumeration<String> attributeNames = request.getSession().getAttributeNames();
		while (attributeNames.hasMoreElements()) {
			String attributeName = attributeNames.nextElement();
			sessionMap.put(attributeName, request.getSession().getAttribute(attributeName));
		}
		return sessionMap;
	}
	
	public static String replaceRestPathVariables(String viewName, Map<String, Object> model, HttpServletRequest request) {
		String newViewName = viewName;
		Matcher matcher = Pattern.compile(PATH_VAR_PATTERN).matcher(newViewName);
		while (matcher.find()) {
			String match = matcher.group();
			String varName = match.substring(1, match.length() - 1);
			String baseVarName = null;
			String subPath = null;
			if (varName.contains(".")) {
				baseVarName = varName.substring(0, varName.indexOf("."));
				subPath = varName.substring(varName.indexOf(".") + 1);
			} else {
				baseVarName = varName;
			}
			Map<String, String> uriTemplateVariables = (Map<String, String>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
			if (!uriTemplateVariables.containsKey(varName))
			{
				Object resolvedObject = model.get(baseVarName);
				if (resolvedObject == null) {
					throw new IllegalArgumentException(varName + " is not present in model.");
				}
				if (subPath != null) {
					BeanWrapperImpl beanWrapper = new BeanWrapperImpl(resolvedObject);
					resolvedObject = beanWrapper.getPropertyValue(subPath);
				}
				if (resolvedObject == null) {
					throw new IllegalArgumentException(varName + " is not present in model.");
				}
				newViewName = newViewName.replace(match, resolvedObject.toString());
			}
			else
			{
				newViewName = newViewName.replace(match, uriTemplateVariables.get(varName));
			}
			matcher.reset(newViewName);
		}
		return newViewName;
	}

	public static String findMatchingRestPath(String[] candidateViewNames, 
			String[] controllerPaths, HttpServletRequest request) {
		String bestMatch = null;
		String requestBestMatchingPattern = 
			(String) request.getAttribute("org.springframework.web.servlet.HandlerMapping.bestMatchingPattern");
		String preferredPath = requestBestMatchingPattern != null ? 
				requestBestMatchingPattern.replaceAll(PATH_VAR_PATTERN, "VAR") : null;
		for (String candidate : candidateViewNames) {
			if (!candidate.contains("=") && bestMatch == null) {
				bestMatch = candidate;
			} else if (preferredPath != null && candidate.contains("=")) {
				String candidatePath = candidate.substring(0, candidate.indexOf("=")).replaceAll(PATH_VAR_PATTERN, "VAR");
				if (controllerPaths == null && candidatePath.equals(preferredPath)) {
					return candidate.substring(candidate.indexOf("=") + 1);
				} else if (controllerPaths != null) {
					for (String controllerPath : controllerPaths) {
						String testPath = (controllerPath + "/" + candidatePath).replace("//", "/");
						if (testPath.equals(preferredPath)) {
							return candidate.substring(candidate.indexOf("=") + 1);
						}
					}
				}
			}
		}
		return bestMatch;
	}
	
	public static String[] getControllerRequestPaths(Object handler) {
		RequestMapping requestMapping = handler.getClass().getAnnotation(RequestMapping.class);
		return requestMapping == null ? null : requestMapping.value();
	}
	
	public static String removeLeadingAndTrailingSlashes(String path) {
		String newPath = path;
		if (newPath.startsWith("/")) {
			newPath = newPath.substring(1);
		}
		if (newPath.endsWith("/")) {
			newPath = newPath.substring(0, newPath.length() - 1);
		}
		return newPath;
	}
	
}
